﻿Module mMagics

    Public Structure TimedMagic

        Dim timer As Long

        Dim magiccode As Integer
        Dim target_type As Protocol.TargetType
        Dim targetparam1 As Int64
        Dim targetparam2 As Int64

        Dim attackeridx As Integer

    End Structure

    '//////////////////////////////////////////////////
    ' Method:    CastMagic
    ' FullName:  mMagics.CastMagic
    ' Access:    public 
    ' Returns:   MagicCast, ByRef Conn As Connection)
    ' Parameter: ByVal cast As MagicCast 
    '               MagicCast containing the spec's of magic to be cast
    ' Parameter: ByRef Conn As Connection
    '               Connection object for the player casting
    '//////////////////////////////////////////////////
    Public Sub CastMagic(ByVal cast As MagicCast, ByRef Conn As Connection)

        'prepare packet to be sent to other local players
        Dim pkt As Packet
        pkt.PacketType = GamePacketType.Magic
        pkt.PacketOpCode = PacketOpCode.Notify
        pkt.Username = DefaultUsername

        'prepare AddMagic struct for the packets
        Dim struct As AddMagic
        struct.dir = cast.dir

        Dim ticker As Long
        Dim x As Integer, y As Integer
        Dim trgetID As Long
        Dim trgetType As Protocol.TargetType
        struct.CharID = Conn.CharacterID

        Dim map As MirMap = GetMapFromConn(Conn)


        Select Case cast.target_type

            Case Protocol.TargetType.Coords
                x = cast.targetparam1
                y = cast.targetparam2


                Dim mob As MirMapMob = map.Mobs.GetMobAtPos(x, y)
                If mob.ID_MOB <> 0 Then
                    If mob.IsAlive Then
                        trgetID = mob.ServerMobID
                        trgetType = Protocol.TargetType.Mob
                        GoTo foundtarget
                    End If
                End If

                Dim player As MirMapPlayer = GetPlayerAtPos(map, x, y)
                If Not player.conn Is Nothing Then
                    trgetID = player.conn.CharacterID
                    trgetType = Protocol.TargetType.Player
                    GoTo foundtarget
                End If

                'todo: targeting pets (find pet at coords)

                trgetType = Protocol.TargetType.Coords

            Case Protocol.TargetType.Player
                SyncLock map.objMapLock

                    For Each player As MirMapPlayer In map.Players

                        If player.conn.CharacterID = cast.targetparam1 Then
                            trgetID = player.conn.CharacterID
                            trgetType = Protocol.TargetType.Player
                            x = player.conn.PositionInfo.INT2_X
                            y = player.conn.PositionInfo.INT2_Y
                            Exit For
                        End If

                    Next

                End SyncLock

            Case Protocol.TargetType.Mob
                Dim mob As MirMapMob = map.Mobs.GetMobFromIndex(cast.targetparam1)

                If mob.ID_MOB <> 0 Then
                    If mob.IsAlive Then
                        trgetID = mob.ServerMobID
                        trgetType = Protocol.TargetType.Mob
                        x = mob.Pos.INT2_X
                        y = mob.Pos.INT2_Y
                    End If
                End If

        End Select

foundtarget:


        Select Case cast.magiccode

            Case Protocol.Magics.HealingCast

                struct.magiccode = Protocol.Magics.HealingCast
                struct.startx = Conn.PositionInfo.INT2_X
                struct.starty = Conn.PositionInfo.INT2_Y

                SendToLocalPlayers(Conn, pkt, struct, True)

            Case Protocol.Magics.HealingEffect

                If trgetType <> Protocol.TargetType.Coords Then

                    AddTimedMagic(Protocol.Magics.HealingEffect, trgetType, trgetID, 0, 0, Conn)
                    DoSkillXP(Conn, Skills.Healing)

                Else

                    AddTimedMagic(Protocol.Magics.HealingEffect, trgetType, x, y, 0, Conn)

                End If

            Case Protocol.Magics.FireballCast

                struct.magiccode = Protocol.Magics.FireballCast
                struct.startx = Conn.PositionInfo.INT2_X
                struct.starty = Conn.PositionInfo.INT2_Y

                SendToLocalPlayers(Conn, pkt, struct, True)

            Case Protocol.Magics.AdamantineFireballCast

                'TODO:Finish Adamantine fireball magic

                'stationary magic (cast effect)

                struct.magiccode = Protocol.Magics.AdamantineFireballCast
                struct.startx = Conn.PositionInfo.INT2_X
                struct.starty = Conn.PositionInfo.INT2_Y

                SendToLocalPlayers(Conn, pkt, struct, True)

            Case Protocol.Magics.AdamantineFireballProjectile

                struct.startx = Conn.PositionInfo.INT2_X
                struct.starty = Conn.PositionInfo.INT2_Y

                struct.magiccode = Protocol.Magics.AdamantineFireballProjectile

                If trgetType = Protocol.TargetType.Coords Then

                    struct.destx = x
                    struct.desty = y

                Else

                    struct.destx = y
                    struct.desty = x

                    AddTimedMagic(Protocol.Magics.AdamantineFireballHit, trgetType, trgetID, 0, ticker, Conn)
                    'DoSkillXP(Conn, Skills.AdamantineFireball)

                End If

                SendToLocalPlayers(Conn, pkt, struct)

            Case Protocol.Magics.FireballProjectile

                struct.startx = Conn.PositionInfo.INT2_X
                struct.starty = Conn.PositionInfo.INT2_Y

                struct.magiccode = Protocol.Magics.FireballProjectile

                If trgetType = Protocol.TargetType.Coords Then

                    struct.destx = cast.targetparam1
                    struct.desty = cast.targetparam2

                Else

                    struct.destx = x
                    struct.desty = y

                    AddTimedMagic(Protocol.Magics.FireballHit, trgetType, trgetID, 0, ticker, Conn)
                    DoSkillXP(Conn, Skills.Fireball)

                End If

                SendToLocalPlayers(Conn, pkt, struct)

            Case Protocol.Magics.Slaying

                struct.startx = Conn.PositionInfo.INT2_X
                struct.starty = Conn.PositionInfo.INT2_Y
                struct.magiccode = Protocol.Magics.Slaying
                struct.dir = Conn.PositionInfo.BYTE_DIRECTION

                SendToLocalPlayers(Conn, pkt, struct)


        End Select

    End Sub

    Private Function CalculateMagicProjectileSpeed(ByVal x As Integer, ByVal y As Integer, ByVal speedxy As MagicAnimSpeedXY, ByVal speed As MagicAnimSpeed) As Integer

        Dim ticker As Long
        Dim sx As Single, sy As Single

        If x < 0 Then x *= -1
        If y < 0 Then y *= -1

        x = x * Constants.TileWidth
        y = y * Constants.TileHeight

        sx = x / (x + y)
        sy = y / (x + y)

        sx *= speedxy
        sy *= speedxy

        If sx <> 0 Then x = x / sx Else x = 0
        If sy <> 0 Then y = y / sy Else y = 0

        If x > y And x > 0 Then
            ticker = x * speed
        Else
            ticker = y * speed
        End If

        If ticker < 0 Then ticker *= -1

        Return ticker

    End Function

    Public Sub DoTimedMagics(ByVal tc As Long)

        'On Error Resume Next


        Dim i As Integer
        Dim tmp As TimedMagic
        Dim mob As MirMapMob

        SyncLock mMaps.objMapsLock

            For Each map As MirMap In mMaps.mMaps
                If map.lstTimedMagics.Count > 0 Then

                    Do Until i = map.lstTimedMagics.Count
                        tmp = map.lstTimedMagics(i)
                        If tmp.timer <= tc Then

                            Dim pkt As Packet
                            pkt.PacketType = GamePacketType.Magic
                            pkt.PacketOpCode = PacketOpCode.Notify
                            pkt.Username = DefaultUsername

                            Dim struct As AddMagic
                            struct.magiccode = tmp.magiccode

                            Dim conn As Connection = Socket.SockServer.ConnectionList.Item(tmp.attackeridx)

                            If tmp.target_type = Protocol.TargetType.Mob Then

                                SyncLock map.objMapLock

                                    mob = map.Mobs.GetMobFromIndex(tmp.targetparam1)
                                    If mob.ID_MOB <> 0 Then

                                        struct.startx = mob.Pos.INT2_X
                                        struct.starty = mob.Pos.INT2_Y
                                        SendToLocalPlayers(mob.Pos.INT2_X, mob.Pos.INT2_Y, map, pkt, struct)

                                        If tmp.magiccode = Protocol.Magics.AdamantineFireballHit Then

                                            PlayerMagicMob(Socket.SockServer.ConnectionList.Item(tmp.attackeridx), mob, 2.8F, Element.Fire, 5.0F)

                                        ElseIf tmp.magiccode = Protocol.Magics.FireballHit Then

                                            Dim lvl As Integer = GetSkillLevel(conn, Skills.Fireball)
                                            PlayerMagicMob(Socket.SockServer.ConnectionList.Item(tmp.attackeridx), mob, 1.0F + (0.2F * lvl), Element.Fire, 3.5F)

                                        ElseIf tmp.magiccode = Protocol.Magics.HealingEffect Then

                                            If mob.VitalPoints.HP < mob.VitalPoints.MAXHP Then

                                                Dim SCroll As Integer
                                                With conn.Stats
                                                    SCroll = CInt(Math.Floor((.INT2_MAX_SC - .INT2_MIN_SC + 1) * Rnd())) + .INT2_MIN_SC
                                                End With

                                                Dim hpgained As Integer = (GetSkillLevel(conn, Skills.Healing) + 1) * SCroll * 2
                                                If hpgained < 0 Then hpgained = 0
                                                mob.VitalPoints.HP += hpgained

                                                If mob.VitalPoints.HP > mob.VitalPoints.MAXHP Then
                                                    mob.VitalPoints.HP = mob.VitalPoints.MAXHP
                                                End If
                                                Dim p As Packet
                                                Dim s As MobHitInfo

                                                p.PacketType = GamePacketType.MobHit
                                                p.PacketOpCode = PacketOpCode.Notify
                                                p.Username = DefaultUsername

                                                s.ServerMobID = mob.ServerMobID
                                                s.VitalPoints = mob.VitalPoints

                                                MobSendToPlayers(mob, p, s)

                                            End If

                                        End If

                                        map.Mobs.SetMob(mob)
                                    End If

                                End SyncLock

                            ElseIf tmp.target_type = Protocol.TargetType.Player Then
                                If tmp.targetparam1 > 0 Then

                                    Dim plyr As Connection = Socket.SockServer.ConnectionList.GetConnFromCharacterID(tmp.targetparam1)
                                    struct.startx = plyr.PositionInfo.INT2_X
                                    struct.starty = plyr.PositionInfo.INT2_Y
                                    SendToLocalPlayers(struct.startx, struct.starty, map, pkt, struct)
                                    'PlayerMagicPlayer(Socket.SockServer.ConnectionList.Item(tmp.attackeridx), mob, 2.8F, Element.Fire, 5.0F)

                                    If tmp.magiccode = Protocol.Magics.AdamantineFireballHit Then

                                        'PlayerMagicMob(Socket.SockServer.ConnectionList.Item(tmp.attackeridx), mob, 2.8F, Element.Fire, 5.0F)

                                    ElseIf tmp.magiccode = Protocol.Magics.FireballHit Then

                                        'PlayerMagicMob(Socket.SockServer.ConnectionList.Item(tmp.attackeridx), mob, 1.4F, Element.Fire, 5.0F)

                                    ElseIf tmp.magiccode = Protocol.Magics.HealingEffect Then

                                        If plyr.VitalPoints.HP < plyr.VitalPoints.MAXHP Then

                                            Dim SCroll As Integer
                                            With conn.Stats
                                                SCroll = CInt(Math.Floor((.INT2_MAX_SC - .INT2_MIN_SC + 1) * Rnd())) + .INT2_MIN_SC
                                            End With

                                            Dim hpgained As Integer = (GetSkillLevel(conn, Skills.Healing) + 1) * SCroll * 2
                                            If hpgained < 0 Then hpgained = 0
                                            plyr.VitalPoints.HP += hpgained

                                            If plyr.VitalPoints.HP > plyr.VitalPoints.MAXHP Then
                                                plyr.VitalPoints.HP = plyr.VitalPoints.MAXHP
                                            End If
                                            UpdatePlayerHealth(plyr, True)
                                        End If

                                    End If

                                End If

                            End If


                            System.Windows.Forms.Application.DoEvents()

                            map.lstTimedMagics.RemoveAt(i)
                            System.Windows.Forms.Application.DoEvents()

                        Else
                            i += 1
                        End If
                    Loop

                End If

            Next

        End SyncLock

    End Sub

    Public Sub AddTimedMagic(ByVal code As Integer, ByVal target_type As Protocol.TargetType, TARGETPARAM1 As Int64, TARGETPARAM2 As Int64, ByVal ticker As Long, ByRef Conn As Connection)

        Dim tmp As TimedMagic

        tmp.magiccode = code
        tmp.target_type = target_type
        tmp.targetparam1 = TARGETPARAM1
        tmp.targetparam2 = TARGETPARAM2
        tmp.timer = DateTime.Now.Ticks + ticker
        tmp.attackeridx = Conn.Index

        SyncLock GetMapFromConn(Conn).objMapLock

            GetMapFromConn(Conn).lstTimedMagics.Add(tmp)

        End SyncLock

    End Sub

    Public Sub TeleportMagic(conn As Connection)

        Dim struct As AddMagic

        struct.startx = conn.PositionInfo.INT2_X
        struct.starty = conn.PositionInfo.INT2_Y
        struct.magiccode = Protocol.Magics.Teleport

        Dim pkt As Packet
        pkt.PacketType = GamePacketType.Magic
        pkt.PacketOpCode = PacketOpCode.Notify
        pkt.Username = DefaultUsername

        SendToLocalPlayers(conn, pkt, struct)

    End Sub

    Public Sub LevelUpMagic(conn As Connection)

        Dim struct As AddMagic

        struct.startx = conn.PositionInfo.INT2_X
        struct.starty = conn.PositionInfo.INT2_Y
        struct.magiccode = Protocol.Magics.LevelUp

        Dim pkt As Packet
        pkt.PacketType = GamePacketType.Magic
        pkt.PacketOpCode = PacketOpCode.Notify
        pkt.Username = DefaultUsername

        SendToLocalPlayers(conn, pkt, struct)

    End Sub

    Public Function GetSkillLevel(ByRef conn As Connection, skill As Protocol.Skills) As Integer

        Dim ssql As String = "SELECT BYTE_LEVEL FROM TBL_SKILLBOOK WHERE ID_CHARACTER=" & conn.CharacterID & " AND BYTE_SKILL=" & skill & ";"
        Dim col As Collection = DB.GameDB.ExecuteQueryFirstRecord(ssql)

        If col Is Nothing Then
            Return -1
        Else
            If col.Count > 0 Then
                Return col("BYTE_LEVEL")
            Else
                Return -1
            End If
        End If

    End Function

    Public Sub LoadPlayerSkills(ByRef conn As Connection)

        Dim sql As String = "SELECT * FROM TBL_SKILLBOOK WHERE ID_CHARACTER=" & conn.CharacterID & ";"

        Dim arr As ArrayList = DB.GameDB.ExecuteQuery(sql)

        Dim p As Packet
        p.Username = conn.Username
        p.PacketType = GamePacketType.AddPlayerSkill
        p.PacketOpCode = EvoLib.Protocol.mProtocol.PacketOpCode.Custom2
        
        Socket.SockServer.SendData(MakePacket(p, Nothing, conn.Crypto), conn)

        p.PacketOpCode = EvoLib.Protocol.mProtocol.PacketOpCode.Notify

        If Not arr Is Nothing Then
            If arr.Count > 0 Then

                For i As Integer = 0 To arr.Count - 1

                    Dim struct As PlayerSkill

                    struct.BYTE_SKILL = arr(i)("BYTE_SKILL")

                    Dim skilldesc As PlayerSkillDesc

                    skilldesc.BYTE_SKILL = arr(i)("BYTE_SKILL")
                    skilldesc.BYTE_LEVEL = arr(i)("BYTE_LEVEL")
                    skilldesc.INT_EXP = arr(i)("INT8_EXP")

                    sql = "SELECT INT8_TOTALEXP FROM TBL_SKILLLEVELS WHERE BYTE_SKILL=" & skilldesc.BYTE_SKILL & " AND BYTE_LEVEL=" & skilldesc.BYTE_LEVEL & ";"
                    Dim row As Collection = DB.GameDB.ExecuteQueryFirstRecord(sql)
                    skilldesc.INT_LEVEL_EXP = row("INT8_TOTALEXP")

                    struct.LEVEL_INFO = skilldesc

                    sql = "SELECT * FROM TBL_SKILLDESC WHERE BYTE_SKILL=" & skilldesc.BYTE_SKILL & ";"
                    row = DB.GameDB.ExecuteQueryFirstRecord(sql)
                    struct.STR_DESC = row("STR_DESC")

                    sql = "SELECT * FROM TBL_SKILLLEVELS WHERE BYTE_SKILL=" & skilldesc.BYTE_SKILL
                    Dim levels As ArrayList = DB.GameDB.ExecuteQuery(sql)

                    If Not levels Is Nothing Then
                        If arr.Count > 0 Then
                            For Each currlvl As Collection In levels
                                Dim tmpLvl As PlayerSkillLevelStat
                                tmpLvl.BYTE_LEVEL = currlvl("BYTE_PLAYERLEVEL")
                                tmpLvl.INT_EXP = currlvl("INT8_TOTALEXP")

                                Select Case currlvl("BYTE_LEVEL")
                                    Case 0
                                        struct.LVL_1 = tmpLvl
                                    Case 1
                                        struct.LVL_2 = tmpLvl
                                    Case 2
                                        struct.LVL_3 = tmpLvl
                                    Case 3
                                        struct.LVL_4 = tmpLvl
                                    Case 4
                                        struct.LVL_5 = tmpLvl

                                End Select

                            Next
                        End If
                    End If

                    sql = "SELECT BYTE_KEY, BYTE_SET FROM TBL_SKILLMAPPINGS WHERE ID_CHARACTER=" & conn.CharacterID & " AND BYTE_SKILL=" & skilldesc.BYTE_SKILL & ";"
                    Dim map As Collection = DB.GameDB.ExecuteQueryFirstRecord(sql)

                    If map.Count = 0 Then
                        struct.KEY_MAP.BYTE_KEY = Protocol.SkillKey.NotSet
                        struct.KEY_MAP.BYTE_SET = Protocol.SkillSet.NotSet
                    Else
                        struct.KEY_MAP.BYTE_KEY = map("BYTE_KEY")
                        struct.KEY_MAP.BYTE_SET = map("BYTE_SET")
                    End If

                    struct.KEY_MAP.BYTE_SKILL = skilldesc.BYTE_SKILL

                    p.Username = conn.Username
                    p.PacketType = GamePacketType.AddPlayerSkill
                    p.PacketOpCode = EvoLib.Protocol.mProtocol.PacketOpCode.Notify
                    
                    Socket.SockServer.SendData(MakePacket(p, struct, conn.Crypto), conn)

                Next

            End If
        End If

    End Sub

    Public Sub DoMapping(conn As Connection, struct As PlayerSkillMapping)

        Dim sSql As String = "DELETE FROM TBL_SKILLMAPPINGS WHERE ID_CHARACTER=" & conn.CharacterID & " AND BYTE_KEY=" & struct.BYTE_KEY & " AND BYTE_SET=" & struct.BYTE_SET & ";"
        DB.GameDB.ExecuteNonQuery(sSql)

        sSql = "SELECT * FROM TBL_SKILLMAPPINGS WHERE ID_CHARACTER=" & conn.CharacterID & " AND BYTE_SKILL=" & struct.BYTE_SKILL & ";"
        Dim row As Collection = DB.GameDB.ExecuteQueryFirstRecord(sSql)

        If row.Count > 0 Then
            sSql = "UPDATE TBL_SKILLMAPPINGS SET BYTE_KEY=" & struct.BYTE_KEY & ", BYTE_SET=" & struct.BYTE_SET & " WHERE ID_CHARACTER=" & conn.CharacterID & " AND BYTE_SKILL=" & struct.BYTE_SKILL & ";"
            DB.GameDB.ExecuteNonQuery(sSql)
        Else
            Dim cols() As String = {"ID_CHARACTER", "BYTE_SKILL", "BYTE_KEY", "BYTE_SET"}
            Dim vals() As String = {conn.CharacterID, struct.BYTE_SKILL, struct.BYTE_KEY, struct.BYTE_SET}
            DB.GameDB.NewRecord("TBL_SKILLMAPPINGS", cols, vals)
        End If

        LoadPlayerSkills(conn)

    End Sub

    Public Sub DoSkillXP(ByRef conn As Connection, ByVal skill As Skills)

        Dim sql As String = "SELECT a.BYTE_LEVEL, a.BYTE_SKILL, b.BYTE_PLAYERLEVEL, b.INT8_TOTALEXP, a.INT8_EXP FROM TBL_SKILLBOOK a JOIN TBL_SKILLLEVELS b ON a.BYTE_SKILL=b.BYTE_SKILL AND a.BYTE_LEVEL=b.BYTE_LEVEL WHERE a.ID_CHARACTER=" & conn.CharacterID & " AND a.BYTE_SKILL=" & skill & ";"
        Dim row As Collection = DB.GameDB.ExecuteQueryFirstRecord(sql)

        If Not row Is Nothing Then
            If row.Count > 0 Then

                Dim currentxp As Int64 = row("INT8_EXP")
                Dim xptolevel As Int64 = row("INT8_TOTALEXP")

                If xptolevel = -1 Then Exit Sub

                Dim xpgained As Int64 = (Int(Rnd() * 3) + 1)
                If xpgained < 0 Then xpgained = 0

                currentxp += xpgained
                If currentxp >= xptolevel Then
                    currentxp -= xptolevel
                    Packets.SendSystemMessage(conn, "Skill " & CType(row("BYTE_SKILL"), Skills).ToString & " leveled up!")
                    sql = "UPDATE TBL_SKILLBOOK SET BYTE_LEVEL=" & (row("BYTE_LEVEL") + 1) & ", INT8_EXP=" & currentxp & " WHERE ID_CHARACTER=" & conn.CharacterID & " AND BYTE_SKILL=" & skill & ";"
                    DB.GameDB.ExecuteNonQuery(sql)
                Else
                    sql = "UPDATE TBL_SKILLBOOK SET INT8_EXP=" & currentxp & " WHERE ID_CHARACTER=" & conn.CharacterID & " AND BYTE_SKILL=" & skill & ";"
                    DB.GameDB.ExecuteNonQuery(sql)
                End If

                Dim p As Packet

                p.PacketType = GamePacketType.AddPlayerSkill
                p.PacketOpCode = EvoLib.Protocol.mProtocol.PacketOpCode.Custom3
                p.Username = conn.Username
                
                Dim struct As PlayerSkillDesc

                sql = "SELECT a.BYTE_SKILL, a.BYTE_LEVEL, a.INT8_EXP, b.INT8_TOTALEXP FROM TBL_SKILLBOOK a JOIN TBL_SKILLLEVELS b ON b.BYTE_SKILL=a.BYTE_SKILL AND b.BYTE_LEVEL=a.BYTE_LEVEL WHERE ID_CHARACTER=" & conn.CharacterID & " AND a.BYTE_SKILL=" & skill & ";"
                Dim row2 As Collection = DB.GameDB.ExecuteQueryFirstRecord(sql)


                struct.BYTE_SKILL = row2("BYTE_SKILL")
                struct.BYTE_LEVEL = row2("BYTE_LEVEL")
                struct.INT_EXP = row2("INT8_EXP")
                struct.INT_LEVEL_EXP = row2("INT8_TOTALEXP")

                Socket.SockServer.SendData(MakePacket(p, struct, conn.Crypto), conn)

            End If
        End If

    End Sub

End Module
